/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright 2009 - 2012 Luca Mingardi.
 *
 * This file is part of jeeObserver.
 *
 * JeeObserver is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * JeeObserver is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */
package jeeobserver;

import java.io.InputStream;
import java.io.Reader;
import java.math.BigDecimal;
import java.net.URL;
import java.sql.Array;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.Connection;
import java.sql.Date;
import java.sql.NClob;
import java.sql.ParameterMetaData;
import java.sql.PreparedStatement;
import java.sql.Ref;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.RowId;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.SQLXML;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.Calendar;

/**
 * The Class JdbcPreparedStatement.
 *
 * @author Luca Mingardi
 * @version 4.0
 */
public class JdbcPreparedStatement implements PreparedStatement {

	private PreparedStatement realPreparedStatement;

	private String connectionUrl;

	private String currentQuery;

	public JdbcPreparedStatement(PreparedStatement realPreparedStatement, String connectionUrl, String query) {
		this.realPreparedStatement = realPreparedStatement;
		this.connectionUrl = connectionUrl;
		this.currentQuery = query;
	}

	@Override
	public ResultSet executeQuery() throws SQLException {

		ActionObserver observer = new ActionObserver(this.connectionUrl, this.currentQuery);
		observer.start();
		ResultSet result = null;
		try {
			result = this.realPreparedStatement.executeQuery();
		} catch (SQLException e) {
			observer.throwException(e);
			throw e;
		} finally {
			observer.stop();
		}

		return result;
	}

	@Override
	public int executeUpdate() throws SQLException {
		ActionObserver observer = new ActionObserver(this.connectionUrl, this.currentQuery);
		observer.start();
		int result = this.realPreparedStatement.executeUpdate();
		observer.stop();

		return result;
	}

	@Override
	public void setNull(int parameterIndex, int sqlType) throws SQLException {
		this.realPreparedStatement.setNull(parameterIndex, sqlType);
		// variables.put(parameterIndex, null);
	}

	@Override
	public void setBoolean(int parameterIndex, boolean x) throws SQLException {
		this.realPreparedStatement.setBoolean(parameterIndex, x);
		// variables.put(parameterIndex, x);
	}

	@Override
	public void setByte(int parameterIndex, byte x) throws SQLException {
		this.realPreparedStatement.setByte(parameterIndex, x);
		// variables.put(parameterIndex, x);
	}

	@Override
	public void setShort(int parameterIndex, short x) throws SQLException {
		this.realPreparedStatement.setShort(parameterIndex, x);
		// variables.put(parameterIndex, x);
	}

	@Override
	public void setInt(int parameterIndex, int x) throws SQLException {
		this.realPreparedStatement.setInt(parameterIndex, x);
		// variables.put(parameterIndex, x);
	}

	@Override
	public void setLong(int parameterIndex, long x) throws SQLException {
		this.realPreparedStatement.setLong(parameterIndex, x);
		// variables.put(parameterIndex, x);
	}

	@Override
	public void setFloat(int parameterIndex, float x) throws SQLException {
		this.realPreparedStatement.setFloat(parameterIndex, x);
		// variables.put(parameterIndex, x);
	}

	@Override
	public void setDouble(int parameterIndex, double x) throws SQLException {
		this.realPreparedStatement.setDouble(parameterIndex, x);
		// variables.put(parameterIndex, x);
	}

	@Override
	public void setBigDecimal(int parameterIndex, BigDecimal x) throws SQLException {
		this.realPreparedStatement.setBigDecimal(parameterIndex, x);
		// variables.put(parameterIndex, x);
	}

	@Override
	public void setString(int parameterIndex, String x) throws SQLException {
		this.realPreparedStatement.setString(parameterIndex, x);
		// variables.put(parameterIndex, x);
	}

	@Override
	public void setBytes(int parameterIndex, byte[] x) throws SQLException {
		this.realPreparedStatement.setBytes(parameterIndex, x);
		// variables.put(parameterIndex, x);
	}

	@Override
	public void setDate(int parameterIndex, Date x) throws SQLException {
		this.realPreparedStatement.setDate(parameterIndex, x);
		// variables.put(parameterIndex, x);
	}

	@Override
	public void setTime(int parameterIndex, Time x) throws SQLException {
		this.realPreparedStatement.setTime(parameterIndex, x);
		// variables.put(parameterIndex, x);
	}

	@Override
	public void setTimestamp(int parameterIndex, Timestamp x) throws SQLException {
		this.realPreparedStatement.setTimestamp(parameterIndex, x);
		// variables.put(parameterIndex, x);
	}

	@Override
	public void setAsciiStream(int parameterIndex, InputStream x, int length) throws SQLException {
		this.realPreparedStatement.setAsciiStream(parameterIndex, x, length);
		// variables.put(parameterIndex, x);
	}

	@Override
	@Deprecated
	public void setUnicodeStream(int parameterIndex, InputStream x, int length) throws SQLException {
		this.realPreparedStatement.setUnicodeStream(parameterIndex, x, length);
		// variables.put(parameterIndex, x);
	}

	@Override
	public void setBinaryStream(int parameterIndex, InputStream x, int length) throws SQLException {
		this.realPreparedStatement.setBinaryStream(parameterIndex, x, length);
		// variables.put(parameterIndex, x);
	}

	@Override
	public void clearParameters() throws SQLException {
		this.realPreparedStatement.clearParameters();
	}

	@Override
	public void setObject(int parameterIndex, Object x, int targetSqlType) throws SQLException {
		this.realPreparedStatement.setObject(parameterIndex, x, targetSqlType);
		// variables.put(parameterIndex, x);
	}

	@Override
	public void setObject(int parameterIndex, Object x) throws SQLException {
		this.realPreparedStatement.setObject(parameterIndex, x);
		// variables.put(parameterIndex, x);
	}

	@Override
	public boolean execute() throws SQLException {
		ActionObserver observer = new ActionObserver(this.connectionUrl, this.currentQuery);
		observer.start();
		boolean result = this.realPreparedStatement.execute();
		observer.stop();

		return result;
	}

	@Override
	public void addBatch() throws SQLException {
		this.realPreparedStatement.addBatch();
	}

	@Override
	public void setCharacterStream(int parameterIndex, Reader reader, int length) throws SQLException {
		this.realPreparedStatement.setCharacterStream(parameterIndex, reader, length);
		// variables.put(parameterIndex, reader);
	}

	@Override
	public void setRef(int parameterIndex, Ref x) throws SQLException {
		this.realPreparedStatement.setRef(parameterIndex, x);
		// variables.put(parameterIndex, x);
	}

	@Override
	public void setBlob(int parameterIndex, Blob x) throws SQLException {
		this.realPreparedStatement.setBlob(parameterIndex, x);
		// variables.put(parameterIndex, x);
	}

	@Override
	public void setClob(int parameterIndex, Clob x) throws SQLException {
		this.realPreparedStatement.setClob(parameterIndex, x);
		// variables.put(parameterIndex, x);
	}

	@Override
	public void setArray(int parameterIndex, Array x) throws SQLException {
		this.realPreparedStatement.setArray(parameterIndex, x);
		// variables.put(parameterIndex, x);
	}

	@Override
	public ResultSetMetaData getMetaData() throws SQLException {
		return this.realPreparedStatement.getMetaData();
	}

	@Override
	public void setDate(int parameterIndex, Date x, Calendar cal) throws SQLException {
		this.realPreparedStatement.setDate(parameterIndex, x, cal);
		// variables.put(parameterIndex, x);
	}

	@Override
	public void setTime(int parameterIndex, Time x, Calendar cal) throws SQLException {
		this.realPreparedStatement.setTime(parameterIndex, x, cal);
		// variables.put(parameterIndex, x);
	}

	@Override
	public void setTimestamp(int parameterIndex, Timestamp x, Calendar cal) throws SQLException {
		this.realPreparedStatement.setTimestamp(parameterIndex, x, cal);
		// variables.put(parameterIndex, x);
	}

	@Override
	public void setNull(int parameterIndex, int sqlType, String typeName) throws SQLException {
		this.realPreparedStatement.setNull(parameterIndex, sqlType, typeName);
		// variables.put(parameterIndex, null);
	}

	@Override
	public void setURL(int parameterIndex, URL x) throws SQLException {
		this.realPreparedStatement.setURL(parameterIndex, x);
		// variables.put(parameterIndex, x);
	}

	@Override
	public ParameterMetaData getParameterMetaData() throws SQLException {
		return this.realPreparedStatement.getParameterMetaData();
	}

	@Override
	public void setRowId(int parameterIndex, RowId x) throws SQLException {
		this.realPreparedStatement.setRowId(parameterIndex, x);
		// variables.put(parameterIndex, x);
	}

	@Override
	public void setNString(int parameterIndex, String value) throws SQLException {
		this.realPreparedStatement.setNString(parameterIndex, value);
		// variables.put(parameterIndex, value);
	}

	@Override
	public void setNCharacterStream(int parameterIndex, Reader value, long length) throws SQLException {
		this.realPreparedStatement.setNCharacterStream(parameterIndex, value, length);
		// variables.put(parameterIndex, value);
	}

	@Override
	public void setNClob(int parameterIndex, NClob value) throws SQLException {
		this.realPreparedStatement.setNClob(parameterIndex, value);
		// variables.put(parameterIndex, value);
	}

	@Override
	public void setClob(int parameterIndex, Reader reader, long length) throws SQLException {
		this.realPreparedStatement.setClob(parameterIndex, reader, length);
		// variables.put(parameterIndex, reader);
	}

	@Override
	public void setBlob(int parameterIndex, InputStream inputStream, long length) throws SQLException {
		this.realPreparedStatement.setBlob(parameterIndex, inputStream, length);
		// variables.put(parameterIndex, inputStream);
	}

	@Override
	public void setNClob(int parameterIndex, Reader reader, long length) throws SQLException {
		this.realPreparedStatement.setNClob(parameterIndex, reader, length);
		// variables.put(parameterIndex, reader);
	}

	@Override
	public void setSQLXML(int parameterIndex, SQLXML xmlObject) throws SQLException {
		this.realPreparedStatement.setSQLXML(parameterIndex, xmlObject);
		// variables.put(parameterIndex, xmlObject);
	}

	@Override
	public void setObject(int parameterIndex, Object x, int targetSqlType, int scaleOrLength) throws SQLException {
		this.realPreparedStatement.setObject(parameterIndex, x, targetSqlType, scaleOrLength);
		// variables.put(parameterIndex, x);
	}

	@Override
	public void setAsciiStream(int parameterIndex, InputStream x, long length) throws SQLException {
		this.realPreparedStatement.setAsciiStream(parameterIndex, x, parameterIndex);
		// variables.put(parameterIndex, x);
	}

	@Override
	public void setBinaryStream(int parameterIndex, InputStream x, long length) throws SQLException {
		this.realPreparedStatement.setBinaryStream(parameterIndex, x, parameterIndex);
		// variables.put(parameterIndex, x);
	}

	@Override
	public void setCharacterStream(int parameterIndex, Reader reader, long length) throws SQLException {
		this.realPreparedStatement.setCharacterStream(parameterIndex, reader, parameterIndex);
		// variables.put(parameterIndex, reader);
	}

	@Override
	public void setAsciiStream(int parameterIndex, InputStream x) throws SQLException {
		this.realPreparedStatement.setAsciiStream(parameterIndex, x);
		// variables.put(parameterIndex, x);
	}

	@Override
	public void setBinaryStream(int parameterIndex, InputStream x) throws SQLException {
		this.realPreparedStatement.setBinaryStream(parameterIndex, x);
		// variables.put(parameterIndex, x);
	}

	@Override
	public void setCharacterStream(int parameterIndex, Reader reader) throws SQLException {
		this.realPreparedStatement.setCharacterStream(parameterIndex, reader);
		// variables.put(parameterIndex, reader);
	}

	@Override
	public void setNCharacterStream(int parameterIndex, Reader value) throws SQLException {
		this.realPreparedStatement.setNCharacterStream(parameterIndex, value);
		// variables.put(parameterIndex, value);
	}

	@Override
	public void setClob(int parameterIndex, Reader reader) throws SQLException {
		this.realPreparedStatement.setClob(parameterIndex, reader);
		// variables.put(parameterIndex, reader);
	}

	@Override
	public void setBlob(int parameterIndex, InputStream inputStream) throws SQLException {
		this.realPreparedStatement.setBlob(parameterIndex, inputStream);
		// variables.put(parameterIndex, inputStream);
	}

	@Override
	public void setNClob(int parameterIndex, Reader reader) throws SQLException {
		this.realPreparedStatement.setNClob(parameterIndex, reader);
		// variables.put(parameterIndex, reader);
	}

	@Override
	public ResultSet executeQuery(String query) throws SQLException {
		ActionObserver observer = new ActionObserver(this.connectionUrl, query);
		observer.start();
		ResultSet result = this.realPreparedStatement.executeQuery(query);
		observer.stop();

		return result;
	}

	@Override
	public int executeUpdate(String query) throws SQLException {
		ActionObserver observer = new ActionObserver(this.connectionUrl, query);
		observer.start();
		int result = this.realPreparedStatement.executeUpdate(query);
		observer.stop();

		return result;
	}

	@Override
	public void close() throws SQLException {
		this.realPreparedStatement.close();
	}

	@Override
	public int getMaxFieldSize() throws SQLException {
		return this.realPreparedStatement.getMaxFieldSize();
	}

	@Override
	public void setMaxFieldSize(int max) throws SQLException {
		this.realPreparedStatement.setMaxFieldSize(max);
	}

	@Override
	public int getMaxRows() throws SQLException {
		return this.realPreparedStatement.getMaxRows();
	}

	@Override
	public void setMaxRows(int max) throws SQLException {
		this.realPreparedStatement.setMaxRows(max);
	}

	@Override
	public void setEscapeProcessing(boolean enable) throws SQLException {
		this.realPreparedStatement.setEscapeProcessing(enable);
	}

	@Override
	public int getQueryTimeout() throws SQLException {
		return this.realPreparedStatement.getQueryTimeout();
	}

	@Override
	public void setQueryTimeout(int seconds) throws SQLException {
		this.realPreparedStatement.setQueryTimeout(seconds);
	}

	@Override
	public void cancel() throws SQLException {
		this.realPreparedStatement.cancel();
	}

	@Override
	public SQLWarning getWarnings() throws SQLException {
		return this.realPreparedStatement.getWarnings();
	}

	@Override
	public void clearWarnings() throws SQLException {
		this.realPreparedStatement.clearWarnings();
	}

	@Override
	public void setCursorName(String name) throws SQLException {
		this.realPreparedStatement.setCursorName(name);
	}

	@Override
	public boolean execute(String query) throws SQLException {
		ActionObserver observer = new ActionObserver(this.connectionUrl, query);
		observer.start();
		boolean result = this.realPreparedStatement.execute(query);
		observer.stop();

		return result;
	}

	@Override
	public ResultSet getResultSet() throws SQLException {
		return this.realPreparedStatement.getResultSet();
	}

	@Override
	public int getUpdateCount() throws SQLException {
		return this.realPreparedStatement.getUpdateCount();
	}

	@Override
	public boolean getMoreResults() throws SQLException {
		return this.realPreparedStatement.getMoreResults();
	}

	@Override
	public void setFetchDirection(int direction) throws SQLException {
		this.realPreparedStatement.setFetchDirection(direction);
	}

	@Override
	public int getFetchDirection() throws SQLException {
		return this.realPreparedStatement.getFetchDirection();
	}

	@Override
	public void setFetchSize(int rows) throws SQLException {
		this.realPreparedStatement.setFetchSize(rows);
	}

	@Override
	public int getFetchSize() throws SQLException {
		return this.realPreparedStatement.getFetchSize();
	}

	@Override
	public int getResultSetConcurrency() throws SQLException {
		return this.realPreparedStatement.getResultSetConcurrency();
	}

	@Override
	public int getResultSetType() throws SQLException {
		return this.realPreparedStatement.getResultSetType();
	}

	@Override
	public void addBatch(String query) throws SQLException {
		this.currentQuery = query;
		this.realPreparedStatement.addBatch(query);
	}

	@Override
	public void clearBatch() throws SQLException {
		this.realPreparedStatement.clearBatch();
	}

	@Override
	public int[] executeBatch() throws SQLException {
		ActionObserver observer = new ActionObserver(this.connectionUrl, this.currentQuery);
		observer.start();
		int[] result = this.realPreparedStatement.executeBatch();
		observer.stop();

		return result;
	}

	@Override
	public Connection getConnection() throws SQLException {
		return this.realPreparedStatement.getConnection();
	}

	@Override
	public boolean getMoreResults(int current) throws SQLException {
		return this.realPreparedStatement.getMoreResults();
	}

	@Override
	public ResultSet getGeneratedKeys() throws SQLException {
		return this.realPreparedStatement.getGeneratedKeys();
	}

	@Override
	public int executeUpdate(String query, int autoGeneratedKeys) throws SQLException {
		ActionObserver observer = new ActionObserver(this.connectionUrl, query);
		observer.start();
		int result = this.realPreparedStatement.executeUpdate(query, autoGeneratedKeys);
		observer.start();

		return result;
	}

	@Override
	public int executeUpdate(String query, int[] columnIndexes) throws SQLException {
		ActionObserver observer = new ActionObserver(this.connectionUrl, query);
		observer.start();
		int result = this.realPreparedStatement.executeUpdate(query, columnIndexes);
		observer.stop();

		return result;
	}

	@Override
	public int executeUpdate(String query, String[] columnNames) throws SQLException {
		ActionObserver observer = new ActionObserver(this.connectionUrl, query);
		observer.start();
		int result = this.realPreparedStatement.executeUpdate(query, columnNames);
		observer.stop();

		return result;
	}

	@Override
	public boolean execute(String query, int autoGeneratedKeys) throws SQLException {
		ActionObserver observer = new ActionObserver(this.connectionUrl, query);
		observer.start();
		boolean result = this.realPreparedStatement.execute(query, autoGeneratedKeys);
		observer.stop();

		return result;
	}

	@Override
	public boolean execute(String query, int[] columnIndexes) throws SQLException {
		ActionObserver observer = new ActionObserver(this.connectionUrl, query);
		observer.start();
		boolean result = this.realPreparedStatement.execute(query, columnIndexes);
		observer.stop();

		return result;
	}

	@Override
	public boolean execute(String query, String[] columnNames) throws SQLException {
		ActionObserver observer = new ActionObserver(this.connectionUrl, query);
		observer.start();
		boolean result = this.realPreparedStatement.execute(query, columnNames);
		observer.stop();

		return result;
	}

	@Override
	public int getResultSetHoldability() throws SQLException {
		return this.realPreparedStatement.getResultSetHoldability();
	}

	@Override
	public boolean isClosed() throws SQLException {
		return this.realPreparedStatement.isClosed();
	}

	@Override
	public void setPoolable(boolean poolable) throws SQLException {
		this.realPreparedStatement.setPoolable(poolable);
	}

	@Override
	public boolean isPoolable() throws SQLException {
		return this.realPreparedStatement.isPoolable();
	}

	@Override
	public <T> T unwrap(Class<T> iface) throws SQLException {
		return this.realPreparedStatement.unwrap(iface);
	}

	@Override
	public boolean isWrapperFor(Class<?> iface) throws SQLException {
		return this.realPreparedStatement.isWrapperFor(iface);
	}

	 @Override
	public void closeOnCompletion() throws SQLException {
		throw new UnsupportedOperationException("Not supported yet.");
	}

	 @Override
	public boolean isCloseOnCompletion() throws SQLException {
		throw new UnsupportedOperationException("Not supported yet.");
	}
}
